from StrategyEngine.TradingAlgorithm import TradingAlgorithm
from StrategyEngine.SimulationParameters import SimulationParameters
from StrategyEngine.TradingCalender import GetCalender
from StrategyEngine.TradingEnvironment import TradingEnvironment
import Analysis.General as General
import StrategyEngine
#from WindPy import w
#w.start()
import Core.Gadget as Gadget
import datetime
import pandas as pd
import numpy as np

# ---Display Ajustment---
pd.set_option('display.max_rows', None)
pd.set_option('display.max_columns', None)

# ---Connecting DataBase---
from Core.Config import *
config = Config()
database = config.DataBase("MySQL")
realtime = config.RealTime(db=0)

# ---Defining some TimePeriod---
onemonth = datetime.timedelta(days=31)
oneseason = datetime.timedelta(days=90)
oneyear = datetime.timedelta(days=365)
oneday = datetime.timedelta(days=1)
fifteenhours = datetime.timedelta(hours=15)

# ---Transformation of Date Form---
def StrtoDataTime(ob):
    if isinstance(ob, list):
        for i in range(len(ob)):
            ob[i] = datetime.datetime.strptime(ob[i], "%Y-%m-%d")
    elif isinstance(ob, str):
        ob = datetime.datetime.strptime(ob, "%Y-%m-%d")
    return ob
def StrfromDataTime(ob):
    if isinstance(ob, list):
        for i in range(len(ob)):
            ob[i] = datetime.datetime.strftime(ob[i], "%Y-%m-%d")
    else:
        ob = datetime.datetime.strftime(ob, "%Y-%m-%d")
    return ob

# ---Trading Days---
#tradingdays = w.tdays("2000-01-01", "2019-09-30", "").Data[0]
tradingdays = pd.read_csv('tradingdays.csv', encoding='gbk', index_col=0)['0'].values
tradingdays = StrtoDataTime(list(tradingdays))

# ---确定依据PB选股的依据日期---
pd_change_date = []
for year in range(2005, 2020):
    pd_change_date.append('{}-04-30'.format(year))
pd_change_date = StrtoDataTime(pd_change_date)

pd_date = []
for i in range(0, len(pd_change_date)):
    while pd_change_date[i] not in tradingdays:
        pd_change_date[i] -= oneday
    pd_change_date[i] += fifteenhours
    pd_date.append(pd_change_date[i])

# ---Select by PB---
def SelectbyPB(date):
    #
    if isinstance(date, str):
        date = StrtoDataTime(date)
    while date not in pd_date:
        date -= oneday

    date = StrfromDataTime(date)
    pb_sql = "SELECT * FROM factor.pb_lf WHERE Date ='{}'".format(date)
    statements = database.FindWithSQL('factor', '', pb_sql)

    data = []
    for statement in statements:
        symbol = statement["Symbol"]
        DateTime = statement['DateTime']
        ReportDate = statement['ReportDate']
        Value = statement['Value']
        data.append([symbol, DateTime, ReportDate,Value])

    df = pd.DataFrame(data,columns=['Symbol','DateTime','ReportDate','Value'])
    df = df[df['Value'] > 0]
    df = df.sort_values(by='Value', ascending=True)
    count = int(0.2*len(df))
    df = df.iloc[:count]

    return df

# ---F-score---

#OperatingProfit1
#growth of OperatingProfit1
#OperatingCashFlow
#(OperatingCashFlow-EBITDA)/TotalShares
#growth of LTDebt/Total Asset
#growth of CurrentAsset/CurrentLiab
#growth of TotalEquity
#growth of ProfitMargin

def InitialData(date):

    selected_stock_byPB = SelectbyPB(date)
    print('---selected_stocks_by_PB---\n', selected_stock_byPB)
    stocklist = selected_stock_byPB['Symbol'].values
    print('---number_of_stocks_selected_by_PB---\n', len(stocklist))

    def FindData(dt):

        #---the Time range of ReleaseDate---
        releasedatelist = [dt]
        for i in range(20):
            releasedatelist.append(dt - i * oneday)
        releasedatelist = StrfromDataTime(releasedatelist)
        #print('---release_date_list---',releasedatelist)

        #---the Time range of ReportDate---
        reportdate = dt - 25*oneday
        reportdatelist = []
        for i in range(30):
            reportdatelist.append(reportdate - i * oneday)
        reportdatelist = StrfromDataTime(reportdatelist)
        #print('---report_date_list---',reportdatelist)

        sql = "SELECT * FROM stock.fundamental WHERE Symbol in {} AND ReleaseDate in {} and ReportDate in {}".format(
            tuple(stocklist), tuple(releasedatelist), tuple(reportdatelist))
        #print('---sql_command---\n', sql)
        data = []
        statements = database.FindWithSQL("stock", "", sql)
        for statement in statements:
            symbol = statement["Symbol"]
            DateTime = statement['DateTime']
            ReportDate = statement['ReportDate']
            OperatingProfit1 = statement['OperatingProfit1']
            OperatingCashFlow = statement['OperatingCashFlow']
            LTDebt = statement['LTDebt']
            TotalAsset = statement['TotalAsset']
            CurrentAsset = statement['CurrentAsset']
            CurrentLiab = statement['CurrentLiab']
            TotalShares = statement['TotalShares']
            ProfitMargin = statement['ProfitMargin']
            #
            data.append(
                [symbol, DateTime, ReportDate, OperatingProfit1, OperatingCashFlow, LTDebt, TotalAsset,
                 CurrentAsset, CurrentLiab, TotalShares, ProfitMargin])
        #
        df = pd.DataFrame(data, columns=["Symbol", 'DateTime', 'ReportDate', 'OperatingProfit1', 'OperatingCashFlow',
                                          'LTDebt', 'TotalAsset', 'CurrentAsset', 'CurrentLiab',
                                         'TotalShares', 'ProfitMargin'])
        df['index'] = df['Symbol']
        df.set_index(['index'],inplace=True)

        return df

    # --- Data Computing ---
    thisyeardata = FindData(date)
    #print('---this_year_data---\n', thisyeardata)
    lastyeardata = FindData(date-oneyear)
    #print('---last_year_data---\n', lastyeardata)
    thisyeardata['growth_OperatingProfit1'] = thisyeardata['OperatingProfit1']-lastyeardata['OperatingProfit1']
    thisyeardata['OCFpS_OperatingProfit1'] = thisyeardata['OperatingCashFlow']-thisyeardata['OperatingProfit1']
    thisyeardata['LTDebt_ratio'] = thisyeardata['LTDebt'] / thisyeardata['TotalAsset']
    lastyeardata['LTDebt_ratio'] = lastyeardata['LTDebt'] / lastyeardata['TotalAsset']
    thisyeardata['growth_LTDebt_ratio'] = thisyeardata['LTDebt_ratio']-lastyeardata['LTDebt_ratio']
    thisyeardata['CC'] = thisyeardata['CurrentAsset'] / thisyeardata['CurrentLiab']
    lastyeardata['CC'] = lastyeardata['CurrentAsset'] / lastyeardata['CurrentLiab']
    thisyeardata['growth_CC'] = thisyeardata['CC']-lastyeardata['CC']
    thisyeardata['growth_TotalShares'] = thisyeardata['TotalShares'] - lastyeardata['TotalShares']
    thisyeardata['growth_ProfitMargin'] = thisyeardata['ProfitMargin'] - lastyeardata['ProfitMargin']
    thisyeardata = thisyeardata.dropna()

    def FindDatafromFactors(date, name):
        factorName = name
        factors = [factorName]
        df = General.Profile(database, date, factors, instruments=None)
        df = df[df['Symbol'].isin(stocklist)]
        df.drop_duplicates(inplace=True)
        return df

    AssetTurnover = FindDatafromFactors(date, 'AssetTurnover_LYR')
    AssetTurnover_lastyear = FindDatafromFactors(date-oneyear, 'AssetTurnover_LYR')
    AssetTurnover_lastyear.columns = ['Symbol', 'AssetTurnover_LYR_lastyear']
    Growth_AssetTurnover = pd.merge(AssetTurnover, AssetTurnover_lastyear, how='outer',on='Symbol')
    Growth_AssetTurnover['Growth_AssetTurnover'] = Growth_AssetTurnover['AssetTurnover_LYR'] - Growth_AssetTurnover['AssetTurnover_LYR_lastyear']

    thisyeardata = pd.merge(thisyeardata,Growth_AssetTurnover,how='outer',on='Symbol')

    return thisyeardata


def Fscore(date):

    initialdata = InitialData(date)

    def score(df, reverse=False):
        score = []
        for each in np.array(df):
            if each == None:
                score.append(None)
            elif float(each) > 0:
                if reverse == True:
                    score.append(0)
                else:
                    score.append(1)
            else:
                if reverse == True:
                    score.append(1)
                else:
                    score.append(0)
        return score

    initialdata['score1'] = score(initialdata['OperatingProfit1'])
    initialdata['score2'] = score(initialdata['growth_OperatingProfit1'])
    initialdata['score3'] = score(initialdata['OperatingCashFlow'])
    initialdata['score4'] = score(initialdata['OCFpS_OperatingProfit1'])
    initialdata['score5'] = score(initialdata['growth_LTDebt_ratio'], reverse=True)
    initialdata['score6'] = score(initialdata['growth_CC'])
    initialdata['score7'] = score(initialdata['growth_TotalShares'], reverse=True)
    initialdata['score8'] = score(initialdata['growth_ProfitMargin'])
    initialdata['score9'] = score(initialdata['Growth_AssetTurnover'])

    initialdata['Fscore'] = initialdata['score1'] + initialdata['score2'] + initialdata['score3'] + initialdata['score4'] \
                            + initialdata['score5'] + initialdata['score6'] + initialdata['score7'] + initialdata['score8'] +initialdata['score9']

    data = initialdata[['Symbol','DateTime','score1','score2','score3','score4','score5','score6','score7','score8','score9','Fscore']]

    return data

# ---adjust date---
adjustdate = []
for year in range(2007, 2018):
    adjustdate.append('{}-05-09'.format(year))
adjustdate = StrtoDataTime(adjustdate)

for i in range(len(adjustdate)):
    while adjustdate[i] not in tradingdays:
        adjustdate[i] -= oneday

for i in range(len(adjustdate)):
    adjustdate[i] += fifteenhours
print('---adjust_date---', adjustdate)

# Define algorithm
def Initialize(api, context):
    print("initialization")
    pass

def HandledData(api, context, data, dt):
    pass


def OnDaily(api, context, dt):

    #
    if dt in adjustdate:
        print("---------------------------------Position Adjustment on " + str(dt),
              "---------------------------------")
        stock_fscore = Fscore(dt)
        stock_fscore.sort_values(by='Fscore', ascending=False, inplace=True)
        print(stock_fscore)
        stock_fscore_point8 = stock_fscore[stock_fscore['score4'] == 1]
        print('--------------------------------Selected Group-----------------------------------\n', stock_fscore_point8)
        symbolsdf = stock_fscore_point8['Symbol'].values

        symbols = []
        for i in range(len(symbolsdf)):
            symbols.append({"Symbol": symbolsdf[i]})

        api.Rebalance(symbols)
    else:
        pass


def OnWeekly(api, context, dt):
    pass

def OnMonthly(api, context, dt):
    pass


def OnMonthlyBegin(api, context, dt):
    pass

# ---交易日历---
tradingCalender = GetCalender("SH")

#
datetime1 = datetime.datetime(2007, 4, 28)
datetime2 = datetime.datetime(2018, 12, 30)

# ---回测参数设置---
simulatorParameters = SimulationParameters(datetime1=datetime1,
                                           datetime2=datetime2,
                                           trading_calendar=tradingCalender,
                                           data_frequency="monthly")
# ---交易环境：市场数据，业绩基准---
tradingEnvironment = TradingEnvironment(benchmark_symbol="000300.SH",
                                        database=database,
                                        realtimeView=realtime,
                                        trading_calendar=tradingCalender)

# ---构建策略---
strategy = TradingAlgorithm(name="TestStrategy",
                            initialize=Initialize,
                            handle_data=HandledData,
                            on_daily=OnDaily,
                            on_weekly=OnWeekly,
                            on_monthly=OnMonthly,
                            on_monthly_begin=OnMonthlyBegin,
                            analyze=StrategyEngine.Analyze,
                            simulator_parameters=simulatorParameters,
                            trading_environment=tradingEnvironment)

# ---策略中需要用到的参数---
context = {}

instruments = Gadget.FindListedInstrument(database, datetime1, datetime2)
context["Instruments"] = instruments

# ---开始回测---
statistics = strategy.Run(context=context)


# ---Print Statistics of Strategy---
# statistics.to_csv('myoutput.csv')


